package com.weimob.mcloud.wechatwork.customer.mq.model;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.weimob.marketing.syswechatworkusermanagement.wechatworkusermanagement.service.user.OrgDepartmentUserExport;
import com.weimob.mcloud.wechatwork.core.adapter.pojo.dto.channel.response.CustomerGroupListResBO;
import com.weimob.mcloud.wechatwork.core.adapter.service.ChannelAdapterService;
import com.weimob.mcloud.wechatwork.core.adapter.service.cdp.CdpUserAdapterService;
import com.weimob.mcloud.wechatwork.core.base.MultiResponse;
import com.weimob.mcloud.wechatwork.core.entity.privatesphere.PrivateSphere;
import com.weimob.mcloud.wechatwork.core.entity.relation.customer.Customer;
import com.weimob.mcloud.wechatwork.core.entity.relation.employee.Employee;
import com.weimob.mcloud.wechatwork.core.entity.relation.group.CustomerGroup;
import com.weimob.mcloud.wechatwork.core.entity.relation.group.CustomerGroupMember;
import com.weimob.mcloud.wechatwork.core.enums.customer.CustomerStatusEnum;
import com.weimob.mcloud.wechatwork.core.enums.customer.ExternalUserTypeEnum;
import com.weimob.mcloud.wechatwork.core.enums.employee.EmployeeActiveStatusEnum;
import com.weimob.mcloud.wechatwork.core.enums.employee.EmployeeStatusEnum;
import com.weimob.mcloud.wechatwork.core.enums.group.GroupAuthorizationStatusEnum;
import com.weimob.mcloud.wechatwork.core.enums.group.GroupMemberTypeEnum;
import com.weimob.mcloud.wechatwork.core.service.employee.EmployeeService;
import com.weimob.mcloud.wechatwork.customer.externalDependencyProxy.usermanagement.EmployeeProxy;
import com.weimob.mcloud.wechatwork.customer.facade.impl.customer.CustomerFacade;
import com.weimob.mcloud.wechatwork.customer.mq.dto.CorpChatGroupDTO;
import com.weimob.mcloud.wechatwork.customer.mq.enumeration.GroupFollowStatusEnum;
import com.weimob.mcloud.wechatwork.customer.service.user.exception.BaseErrorCode;
import com.weimob.mcloud.wechatwork.customer.service.user.exception.BizException;
import com.weimob.mcloud.wechatwork.customer.third.PrivateSphereThird;
import com.weimob.mcloud.wechatwork.customer.third.core.CustomerGroupExportServiceProxy;
import com.weimob.mcloud.wechatwork.customer.third.core.CustomerServiceProxy;
import com.weimob.mcloud.wechatwork.customer.third.customer.CustomerThird;
import com.weimob.mcloud.wechatwork.statistics.service.CustomerGroupOverviewCurrentStatisticExport;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * @author yuzhaoyang
 * @desc 客户群相关门面服务
 * @date 2021-02-08 14:25
 */
@Service
@Slf4j
public class ChatGroupFacade {
    /**
     * 远程调用腾讯客户群列表超过限制所报错误码
     */
    public static final long REMOTE_WECHATWORK_GROUP_CHAT_LIST_MAX_LIMIT = 81017L;


    @Autowired
    private RemoteChatGroupServiceProxy remoteChatGroupServiceProxy;

    @Autowired
    ChannelAdapterService channelAdapterService;

    @Autowired
    CorpChatGroupServiceImpl corpChatGroupService;

    @Reference
    EmployeeService employeeService;

    @Value("${init.group.partition.count:50}")
    private Integer partitionCount;

    @Resource(name = "customerGroupInitThreadPool")
    ThreadPoolTaskExecutor customerGroupInitThreadPool;

    @Reference
    CustomerGroupOverviewCurrentStatisticExport statisticExport;

    @Resource
    CustomerFacade customerFacade;

    @Reference
    private CdpUserAdapterService cdpUserAdapterService;

    @Reference
    OrgDepartmentUserExport orgDepartmentUserExport;

    @Autowired
    private CustomerThird customerThird;

    @Resource
    CustomerGroupExportServiceProxy customerGroupExportServiceProxy;

    @Autowired
    private CustomerServiceProxy customerServiceProxy;

    @Resource
    RedisTemplate<String,String> stringRedisTemplate;

    @Resource
    private PrivateSphereThird privateSphereThird;

    @Autowired
    private EmployeeProxy employeeProxy;

    /**
     * 初始化客户群数据
     * @param corpId 保留corpId 为了兼容单bos场景
     * @param bosId
     */
    public void initChatGroupData(String corpId, Long bosId) {
        // check
        if (Objects.isNull(bosId) && Objects.isNull(corpId)) {
            log.error("initChatGroupData 失败 corpId&bosId都为空");
        }
        log.info("准备初始化群数据，corpId : {}，bosId : {}", corpId, bosId);

        // 查询私域信息
        PrivateSphere privateSphere = null;
        if (Objects.nonNull(bosId)) {
            privateSphere = privateSphereThird.fetchByBosId(bosId);
        } else {
            privateSphere = privateSphereThird.fetch(corpId);
        }
        if (Objects.isNull(privateSphere)) {
            log.error("initChatGroupData 失败 未查到私域信息 corpId : {}，bosId : {}", corpId, bosId);
            return;
        }

        // 1.初始化预估：一般单个企业拥有的客户群数量在2000以内
        List<CustomerGroupListResBO.GroupChat> groupChatList = Lists.newArrayListWithCapacity(2000);
        // 2.远程从企业微信open api获取客户群列表数据
        try {
            // 获取客户群列表
            groupChatList.addAll(remoteChatGroupServiceProxy.getAllCorpGroupChatList(privateSphere,null));
        }
        // 3.产生异常的时候，先判断一下是不是因为一次从腾讯查的太多
        catch (BizException e) {
            // a.如果错误类型为：超过腾讯单次查询的数据包大小。要对员工分页，每次最多用100个员工，去拉取他们作为群主的客户群列表
            if (e.getErrcode() == REMOTE_WECHATWORK_GROUP_CHAT_LIST_MAX_LIMIT) {
                fetchGroupListByEmployeeId(groupChatList, privateSphere);
            }
            // b.如果错误类型不是因为一次从腾讯查的太多，那就抛出这个异常
            else {
                log.warn("initChatGroupData,BizException, error: ", e);
                throw e;
            }
        }
        catch (Exception e) {
            log.warn("initChatGroupData,Exception, error: ", e);
            throw new BizException(BaseErrorCode.FAILED);
        }

        // 5.同步客户群数据
        syncData(privateSphere, groupChatList);
    }

    /**
     * 根据员工Id 滚动拉取客户群列表
     *
     * @param groupChatList
     */
    private void fetchGroupListByEmployeeId(List<CustomerGroupListResBO.GroupChat> groupChatList, PrivateSphere privateSphere) {
        // 1.查询私域下所有员工
        int pageNum = 1;
        int pageSize = 100;
        MultiResponse<Employee> multiResponse;
        do {
            multiResponse = employeeProxy.fetchEmployee(privateSphere, pageSize, pageNum);
            pageNum++;
            List<Employee> dataList = multiResponse.getDataList();
            if (CollectionUtils.isEmpty(dataList)) {
                break;
            }
            List<String> ownerList = dataList.stream().map(Employee::getTxUserId).distinct().collect(Collectors.toList());
            // 2.根据群主idList，查询他们拥有的客户群列表
            groupChatList.addAll(remoteChatGroupServiceProxy.getAllCorpGroupChatList(privateSphere, ownerList));
        } while (multiResponse.getTotalPage() != null && pageNum <= multiResponse.getTotalPage());
    }


    /**
     * 同步客户群数据
     *
     * @param groupChatList
     */
    public void syncData(PrivateSphere privateSphere, List<CustomerGroupListResBO.GroupChat> groupChatList) {
        if (CollectionUtils.isEmpty(groupChatList) || StringUtils.isEmpty(privateSphere.getCorpId())) {
            return;
        }

        // 过滤掉待继承，继承进行中的群
        List<CustomerGroupListResBO.GroupChat> filterGroupChatList = groupChatList.stream()
                .filter(e -> GroupFollowStatusEnum.FOLLOW_NORMAL.getCode().equals(e.getStatus())
                        || GroupFollowStatusEnum.FOLLOW_DIMISSION_EXTENDED.getCode().equals(e.getStatus()))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(filterGroupChatList) ) {
            return;
        }

        List<List<CustomerGroupListResBO.GroupChat>> partitions = Lists.partition(filterGroupChatList, partitionCount);
        for (List<CustomerGroupListResBO.GroupChat> groupChats : partitions) {
            CompletableFuture<Object>[] completableFutures = new CompletableFuture[groupChats.size()];
            for (int i = 0; i < groupChats.size(); i++) {
                int finalI = i;
                completableFutures[i] = CompletableFuture.supplyAsync(() -> {
                    CorpChatGroupDTO corpChatGroupDTO = new CorpChatGroupDTO();
                    corpChatGroupDTO.setCorpId(privateSphere.getCorpId());
                    corpChatGroupDTO.setChatId(groupChats.get(finalI).getChatId());
                    // 历史数据同步标识
                    corpChatGroupDTO.setIsInitGroup(true);
                    try {
                        corpChatGroupService.create(corpChatGroupDTO,privateSphere);
                    } catch (Exception e) {
                        log.error("syncDateError:{}, info:{}", e, JSON.toJSONString(corpChatGroupDTO));
                    }
                    return null;
                }, customerGroupInitThreadPool);
            }
            // 等待执行完成
            CompletableFuture.allOf(completableFutures).join();
        }
    }

    /**
     * 客户群维护好友关系
     *
     * @param externalUserId
     * @param needQuery
     */
    public void execute(PrivateSphere privateSphere, String externalUserId,String customerId, boolean needQuery ) {
        boolean isFriend;
        if(needQuery){
            // 1、判断好友关系
            if(StringUtils.isBlank(externalUserId) || StringUtils.isBlank(customerId)){
                log.error("ChatGroupFacade.execute 判断好友关系失败externalUserId或customerId为空 externalUserId : {}, customerId : {}",externalUserId,customerId);
                return;
            }
            isFriend = isFriend(privateSphere, customerId);
        }else {
            isFriend = true;
        }

        // 2、corp_id + externalUserId 更新 客户群成员表 是否企微好友字段
        CustomerGroupMember customerGroupMember = new CustomerGroupMember();
        customerGroupMember.setPrivateSphere(privateSphere);
        customerGroupMember.setIsFriend(isFriend ? 1 : 0);
        customerGroupMember.setGroupMemberType(GroupMemberTypeEnum.EXTERNAL_USER);
        customerGroupMember.setMemberId(externalUserId);
        customerGroupExportServiceProxy.updateMemberFriendStatus(customerGroupMember, null);
    }

    /**
     * 判断好友关系
     * @param privateSphere
     * @param customerId
     * @return true 好友 false 非好友
     */
    public boolean isFriend(PrivateSphere privateSphere, String customerId) {
        if(Objects.isNull(privateSphere) || StringUtils.isBlank(customerId)){
            return false;
        }
        // 判断企业微信客户
        Customer customer = customerServiceProxy.fetchCustomer(customerId, null, privateSphere);
        if(Objects.isNull(customer) || ExternalUserTypeEnum.WECHATWORK_CUSTOMER.equals(customer.getExternalUserType())){
            return false;
        }
        // 判断好友关系
        Long friendNo =customerServiceProxy.fetchCustomerEmployeeRelationCount(privateSphere.getCorpId(),customerId,true,CustomerStatusEnum.DEFAULT);

        return friendNo > 0L;
    }

    public void syncCustomerGroupToES(PrivateSphere privateSphere) {
        // 循环查询客户群数据
        int pageNum = 1;
        int pageSize = 50;
        CustomerGroup customerGroup = new CustomerGroup();
        customerGroup.setPrivateSphere(privateSphere);
        HashMap<String,Object> extInfo = new HashMap<>();
        extInfo.put("orderBy","group_create_time");
        extInfo.put("direction","desc");
        MultiResponse<CustomerGroup> multiResponse;
        // 分页查询客户群信息
        do {
            multiResponse = customerGroupExportServiceProxy.fetchCustomerGroupList(customerGroup, extInfo, null, null, false,true, pageNum, pageSize,false);
            List<CustomerGroup> dataList = multiResponse.getDataList();
            if(CollectionUtils.isEmpty(dataList)){
                break;
            }
            for (CustomerGroup group : dataList) {
                group.setPrivateSphere(privateSphere);
                customerGroupExportServiceProxy.saveCustomerGroup2ES(group);
            }
            pageNum++;
        } while (Objects.nonNull(multiResponse.getTotalPage()) && pageNum <= multiResponse.getTotalPage());
    }

    /**
     * 处理历史数据客户群授权状态
     * 查询群主数据-【未查到群主】或者群主状态为【未授权】【无坐席】时将群状态改为未授权
     */
    public void syncGroupAuthorizationStatus(PrivateSphere privateSphere) {
        int pageNum = 1;
        int pageSize = 50;
        // 1、查询授权状态正常的客户群
        CustomerGroup customerGroup = new CustomerGroup();
        customerGroup.setPrivateSphere(privateSphere);
        HashMap<String,Object> extInfo = new HashMap<>();
        extInfo.put("orderBy", "id");
        extInfo.put("direction", "asc");
        MultiResponse<CustomerGroup> multiResponse;
        do {
            multiResponse = customerGroupExportServiceProxy.fetchCustomerGroupList(customerGroup, extInfo, null, null, false, true, pageNum, pageSize,false);
            List<CustomerGroup> dataList = multiResponse.getDataList();
            if (CollectionUtils.isEmpty(dataList)) {
                break;
            }
            List<CustomerGroup> pendingGroupList = new ArrayList<>();
            for (CustomerGroup group : dataList) {
                // 2、判断群主状态
                Employee owner = (Employee) group.getOwner();
                if (Objects.nonNull(owner) && Boolean.FALSE.equals(owner.getDeleted()) && EmployeeStatusEnum.ACTIVATED.equals(owner.getStatus()) && EmployeeActiveStatusEnum.ACTIVE.equals(owner.getActiveStatus())) {
                    group.setAuthorizationStatus(GroupAuthorizationStatusEnum.AUTHORIZED);
                } else {
                    group.setAuthorizationStatus(GroupAuthorizationStatusEnum.UNAUTHORIZED);
                }
                group.setPrivateSphere(privateSphere);
                pendingGroupList.add(group);
            }
            // 3、修改客户群授权状态
            customerGroupExportServiceProxy.updateGroupAuthorizationStatus(pendingGroupList);
            pageNum++;
        } while (Objects.nonNull(multiResponse.getTotalPage()) && pageNum <= multiResponse.getTotalPage());
    }
}
